package com.cn.serverline.lineicp.productionLine.service.impl;

import cn.hutool.core.collection.CollStreamUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNode;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.stylefeng.roses.kernel.rule.exception.base.ServiceException;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.cn.serverline.lineicp.productionLine.constants.TreeConstants;
import com.cn.serverline.lineicp.productionLine.entity.ProductMix;
import com.cn.serverline.lineicp.productionLine.entity.ProductionProcesses;
import com.cn.serverline.lineicp.productionLine.enums.ProcessStageEnum;
import com.cn.serverline.lineicp.productionLine.enums.ProductMixCategoryEnum;
import com.cn.serverline.lineicp.productionLine.enums.ProductMixException;
import com.cn.serverline.lineicp.productionLine.mapper.ProductMixMapper;
import com.cn.serverline.lineicp.productionLine.mapper.ProductionProcessesMapper;
import com.cn.serverline.lineicp.productionLine.param.*;
import com.cn.serverline.lineicp.productionLine.service.IProductMixService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;


/**
 * <p>
 * 产品结构表 服务实现类
 * </p>
 *
 * @author Gmr
 * @since 2024-07-30
 */
@Service("ProductMixServiceImpl")
public class ProductMixServiceImpl extends ServiceImpl<ProductMixMapper, ProductMix> implements IProductMixService {

    // 工序Mapper
    @Resource(name = "productionProcessesMapper")
    private ProductionProcessesMapper productionProcessesMapper;

    /**
     * 获取树形结构
     *
     * @author Gmr
     * @since 2024/7/30
     * @param productMixTreeParam 产品结构树形结构查询参数
     * @return Tree<Long> 产品结构树形结构数据
     */
    @Override
    public List<Tree<Long>> tree(ProductMixTreeParam productMixTreeParam) {

        // 获取该显示的产品结构
        List<ProductMix> productMixList = this.list(Wrappers.<ProductMix>lambdaQuery()
                .like(StrUtil.isNotBlank(productMixTreeParam.getName()),ProductMix::getName, productMixTreeParam.getName()));

        if(CollectionUtil.isEmpty(productMixList)){
            return Collections.emptyList();
        }

        // 获取零组件ID
        List<Long> productMixIds = productMixList.stream()
                .filter(productMix -> ProductMixCategoryEnum.PROCESS_SCHEME
                        .getCode().equals(productMix.getCategory()))
                .map(ProductMix::getId)
                .collect(Collectors.toList());

        // 补全父节点
        replenishParentProductMix(productMixList);

        List<TreeNode<Long>> treeNodes = buildTreeNodes(productMixList);

        // 如果零组件ID列表不为空，并且需要工序，则添加工序
        if(CollectionUtil.isNotEmpty(productMixIds) && productMixTreeParam.isQueryProcess()){
            addProcessNodes(treeNodes, productMixIds);
        }
        return TreeUtil.build(treeNodes, TreeConstants.ROOT_NODE_PARENT_ID);
    }

    /**
     * 获取全部型号
     *
     * @author Gmr
     * @since 2024/7/30
     * @return List<ProductMix> 型号列表
     */
    @Override
    public List<ProductMix> listModel() {
        return this.list(Wrappers.<ProductMix>lambdaQuery()
                .eq(ProductMix::getCategory, ProductMixCategoryEnum.MODEL.getCode()));
    }

    /**
     * 添加产品类型或型号
     *
     * @author Gmr
     * @since 2024/7/30
     * @param productsAddParam 产品类型和型号添加参数
     */
    @Override
    public void addProducts(ProductsAddParam productsAddParam){

        checkCategory(productsAddParam.getCategory());

        if(ProductMixCategoryEnum.PROCESS_SCHEME.getCode().equals(productsAddParam.getCategory())) {
            throw new ServiceException(ProductMixException.CANNOT_ADD_PROCESS_SCHEME);
        }

        // 如果不为大类，不可为顶级节点
        if(!ProductMixCategoryEnum.LARGE_CATEGORY.getCode().equals(productsAddParam.getCategory()) &&
                TreeConstants.ROOT_NODE_PARENT_ID.equals(productsAddParam.getParentId())){
            throw new ServiceException(ProductMixException.CANNOT_BE_TOP_LEVEL);
        }

        // 判断名称是否重复
        checkDuplicateName(productsAddParam.getName());

        // 如果父节点ID不为空，则检查父节点是否存在
        checkParentNodeExist(productsAddParam.getParentId());

        ProductMix productMix = BeanUtil.copyProperties(productsAddParam, ProductMix.class);
        productMix.setParentId(ProductMixCategoryEnum.LARGE_CATEGORY.getCode()
                .equals(productsAddParam.getCategory()) ? TreeConstants.ROOT_NODE_PARENT_ID : productsAddParam.getParentId());

        this.save(productMix);
    }

    /**
     * 添加工艺方案
     *
     * @author Gmr
     * @since 2024/7/30
     * @param processSchemeAddParam 工艺方案添加参数
     */
    @Override
    public void addProcessScheme(ProcessSchemeAddParam processSchemeAddParam){

        if(TreeConstants.ROOT_NODE_PARENT_ID.equals(processSchemeAddParam.getParentId())){
            throw new ServiceException(ProductMixException.CANNOT_BE_TOP_LEVEL);
        }

        // 判断名称是否重复
        checkDuplicateName(processSchemeAddParam.getName());

        // 校验工艺阶段
        ProcessStageEnum.validate(processSchemeAddParam.getProcessStage());

        // 如果父节点ID不为空，则检查父节点是否存在
        ProductMix spareParts = checkParentNodeExist(processSchemeAddParam.getParentId());
        ProductMix productMix = BeanUtil.copyProperties(processSchemeAddParam, ProductMix.class);
        productMix.setProductCode(spareParts.getName());
        productMix.setVersion(BigDecimal.valueOf(1.0));
        productMix.setCategory(ProductMixCategoryEnum.PROCESS_SCHEME.getCode());
        this.save(productMix);
    }

    /**
     * 获取详细信息
     *
     * @author Gmr
     * @since 2024/7/30
     * @param id ID
     * @return ProductMix 产品结构详细信息
     */
    @Override
    public ProductMix detail(ProductMixIdParam id) {
        return getInfo(id.getId());
    }

    /**
     * 修改工艺方案
     *
     * @author Gmr
     * @since 2024/7/30
     * @param processSchemeEditParam 工艺方案修改参数
     */
    @Override
    public void editProcessScheme(ProcessSchemeEditParam processSchemeEditParam) {
        // 校验修改对象是否存在
        ProductMix productMix = getInfo(processSchemeEditParam.getId());

        // 检查修改名称是否重复
        checkEditNameDuplicate(processSchemeEditParam.getName(), processSchemeEditParam.getId());

        // 检查工艺阶段是否合法
        ProcessStageEnum.validate(processSchemeEditParam.getProcessStage());

        BeanUtil.copyProperties(processSchemeEditParam, productMix);
        this.updateById(productMix);
    }

    /**
     * 修改产品类型或型号
     *
     * @author Gmr
     * @since 2024/7/30
     * @param productsEditParam 产品类型和型号修改参数
     */
    @Override
    public void editProducts(ProductsEditParam productsEditParam) {
        // 校验修改对象是否存在
        ProductMix productMix = getInfo(productsEditParam.getId());

        // 检查修改名称是否重复
        checkEditNameDuplicate(productsEditParam.getName(), productsEditParam.getId());
        BeanUtil.copyProperties(productsEditParam, productMix);
        this.updateById(productMix);
    }

    /**
     * 删除产品类型或型号或工艺方案
     *
     * @author Gmr
     * @since 2024/7/30
     * @param id 对象id集合
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void del(ProductMixIdParam id) {

        // 检验对象是否存在
        getInfo(id.getId());

        // 获取该id下的所有节点
        List<ProductMix> productMixList = this.getBaseMapper().getTreeById(id.getId());

        // 获取该id下的所有零组件
        List<ProductMix> processSchemeList = productMixList.stream().filter(productMix -> ProductMixCategoryEnum.PROCESS_SCHEME.getCode()
                .equals(productMix.getCategory())).collect(Collectors.toList());

        // 检查该id下的所有零组件是否关联了工序
        validateNoRelatedProcesses(processSchemeList);

        this.removeByIds(productMixList.stream().map(ProductMix::getId).collect(Collectors.toList()));
    }

    /**
     * 检查是否存在关联的工序
     *
     * @param processSchemeList 零组件列表
     */
    private void validateNoRelatedProcesses(List<ProductMix> processSchemeList) {
        if (CollectionUtil.isNotEmpty(processSchemeList)) {
            List<Long> processSchemeIds = processSchemeList.stream()
                    .map(ProductMix::getId)
                    .collect(Collectors.toList());

            List<ProductionProcesses> productionProcesses = productionProcessesMapper.selectList(
                    Wrappers.<ProductionProcesses>lambdaQuery().in(ProductionProcesses::getProductMixId, processSchemeIds));

            if (CollectionUtil.isNotEmpty(productionProcesses)) {
                throw new ServiceException(ProductMixException.CANNOT_BE_DELETED);
            }
        }
    }

    /**
     * 添加工艺节点
     *
     * @param treeNodes 树节点列表
     * @param productMixIds 产品结构ID列表
     */
    private void addProcessNodes(List<TreeNode<Long>> treeNodes, List<Long> productMixIds) {
        List<ProductionProcesses> productionProcessesList = productionProcessesMapper.selectList(Wrappers.<ProductionProcesses>lambdaQuery()
                .in(ProductionProcesses::getProductMixId, productMixIds)
                .orderByAsc(ProductionProcesses::getSort));

        if (CollectionUtil.isNotEmpty(productionProcessesList)) {
            treeNodes.addAll(productionProcessesList.stream()
                    .map(productionProcesses -> new TreeNode<>(
                            productionProcesses.getId(),
                            productionProcesses.getProductMixId(),
                            productionProcesses.getProcessName(),
                            productionProcesses.getCreateTime())
                            .setExtra(JSONUtil.parseObj(productionProcesses)))
                    .collect(Collectors.toList()));
        }
    }

    /**
     * 构建树节点列表
     *
     * @param productMixList 产品结构列表
     * @return List<TreeNode<Long>>
     */
    private List<TreeNode<Long>> buildTreeNodes(List<ProductMix> productMixList) {
        return productMixList.stream()
                .map(productMix -> new TreeNode<>(
                        productMix.getId(),
                        productMix.getParentId(),
                        productMix.getName(),
                        productMix.getCreateTime())
                        .setExtra(JSONUtil.parseObj(productMix))
                ).collect(Collectors.toList());
    }

    /**
     * 补全缺失的父级节点
     *
     * @author Gmr
     * @since  2024/7/30
     * @param productMixList 产品结构列表
     */
    private void replenishParentProductMix(List<ProductMix> productMixList){
        // 查询出此模块下所有产品结构
        List<ProductMix> productMixes = this.list();
        // 添加缺失的父级节点
        addMissingProductMix(productMixList, productMixes);
    }

    /**
     * 添加缺失的父级节点
     *
     * @author Gmr
     * @date 2024/7/30
     * @param productMixList 产品结构列表
     * @param allProductMix 所有产品结构
     */
    private void addMissingProductMix(List<ProductMix> productMixList, List<ProductMix> allProductMix){
        // 构建产品结构Map,提高获取缺失父级节点的速度
        Map<Long, ProductMix> menuMap = allProductMix.stream().collect(Collectors.toMap(ProductMix::getId, Function.identity()));

        // 补充查出来的数据缺失的父级节点,让缺失上级的可以构建显示
        Set<Long> parentIds = CollStreamUtil.toSet(productMixList, ProductMix::getParentId);
        Set<Long> ids = CollStreamUtil.toSet(productMixList, ProductMix::getId);

        // 缺失的父级节点Id
        List<Long> deficiencyIds = parentIds.stream()
                .filter(parentId -> !ids.contains(parentId))
                .collect(Collectors.toList());

        // 如果有缺失的父级节点，则继续执行
        if(CollectionUtil.isNotEmpty(deficiencyIds)) {

            // 获取缺失的父级节点
            List<ProductMix> deficiencyMenus = deficiencyIds.stream()
                    .map(menuMap::get)
                    .filter(ObjectUtil::isNotEmpty)
                    .collect(Collectors.toList());

            // 用递归的方式继续添加缺失的父级节点的父级
            if(CollectionUtil.isNotEmpty(deficiencyMenus)){
                addMissingProductMix(deficiencyMenus, allProductMix);
            }
            // 添加缺失的父级节点
            productMixList.addAll(deficiencyMenus);
        }
    }

    /**
     * 检查修改名称是否重复
     *
     * @author Gmr
     * @since 2024/7/30
     * @param name 名称
     * @param id 对象id
     */
    private void checkEditNameDuplicate(String name, Long id) {
        boolean isDuplicate = this.count(Wrappers.<ProductMix>lambdaQuery()
                .eq(ProductMix::getName, name)
                .ne(ProductMix::getId, id)) > 0;

        if(isDuplicate){
            throw new ServiceException(ProductMixException.NAME_DUPLICATE);
        }
    }

    /**
     * 获取信息
     *
     * @author Gmr
     * @since 2024/7/30
     * @param id ID
     * @return ProductMix 产品结构信息
     */
    private ProductMix getInfo(Long id){
        if(ObjectUtil.isEmpty(id)){
            throw new ServiceException(ProductMixException.ID_NOT_NULL);
        }
        ProductMix productMix = this.getById(id);
        if(ObjectUtil.isEmpty(productMix)){
            throw new ServiceException(ProductMixException.NOT_EXIST);
        }
        return productMix;
    }

    /**
     * 检查名称是否重复
     *
     * @author Gmr
     * @since 2024/7/30
     * @param name 名称
     */
    private void checkDuplicateName(String name){
        // 判断名称是否重复
        boolean isDuplicate = this.count(Wrappers.<ProductMix>lambdaQuery()
                .eq(ProductMix::getName, name)) > 0;
        if(isDuplicate){
            throw new ServiceException(ProductMixException.NAME_DUPLICATE);
        }
    }

    /**
     * 检查类别
     *
     * @author Gmr
     * @since 2024/7/30
     * @param category 分类
     */
    private static void checkCategory(Long category) {
        // 判断类别是否为空
        if(ObjectUtil.isEmpty(category)){
            throw new ServiceException(ProductMixException.CATEGORY_NOT_NULL);
        }

        // 校验类别是否合法
        ProductMixCategoryEnum.validate(category);
    }

    /**
     * 检查父节点是否存在
     *
     * @author Gmr
     * @since 2024/7/30
     * @param parentId 父节点ID
     */
    private ProductMix checkParentNodeExist(Long parentId){
        ProductMix productMix = new ProductMix();
        // 如果父节点ID不为空，则检查父节点是否存在
        if(!TreeConstants.ROOT_NODE_PARENT_ID.equals(parentId)){
            try {
                productMix = getInfo(parentId);
            } catch (ServiceException e) {
                throw new ServiceException(ProductMixException.PARENT_NODE_EXIST);
            }
        }
        return productMix;
    }
}
